Skip to content

Add HttpFilterIntegrationTestBase#517

Merged
dubious90 merged 10 commits intoenvoyproxy:masterfrom
oschaaf:extension-refactor-prelude
Sep 16, 2020
Merged

Add HttpFilterIntegrationTestBase#517
dubious90 merged 10 commits intoenvoyproxy:masterfrom
oschaaf:extension-refactor-prelude

Conversation

@oschaaf
Copy link
Copy Markdown
Member

@oschaaf oschaaf commented Sep 9, 2020

Prelude to #512, which includes it and provides a means to
see the end goal.

  • Extracts shared needs between tested extensions.
  • Sanitized api, better naming.
  • Doc comments.
  • Support for testing POST request methods and entity bodies
    to check the alternative flow that will trigger in extensions
    when using that.

Signed-off-by: Otto van der Schaaf oschaaf@we-amp.com

- Extracts shared needs between tested extensions.
- Sanitized api, better naming.
- Doc comments.
- Support for testing POST request methods and entity bodies
  to check the alternative flow that will trigger in extensions
  when using that.

Prelude to envoyproxy#512, which includes it and provides a means to
see the end goal.

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@dubious90
Copy link
Copy Markdown
Contributor

@eric846 please review and assign to me when done.

@dubious90 dubious90 requested a review from eric846 September 9, 2020 17:42
…r-prelude

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
* up a fake upstream to supply the response.
*
* @param request_level_config Configuration to be delivered by request header. For example
* "{{response_body_size:1024}".
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a typo.

* extension under test should supply the response.
*
* @param request_level_config Configuration to be delivered by request header. For example
* "{{response_body_size:1024}".
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo?

}

Envoy::IntegrationStreamDecoderPtr
HttpFilterIntegrationTestBase::getResponse(absl::string_view request_level_config,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we replace this getResponse overload with a helper that translates a string into an Envoy::Http::TestRequestHeaderMapImpl and use the helper inline?

* @brief Fetches a response with request-level configuration set in the request header.
*
* @param request_level_config Configuration to be delivered by request header. For example
* "{{response_body_size:1024}".
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo?

getResponse(const Envoy::Http::TestRequestHeaderMapImpl& request_headers,
bool setup_for_upstream_request);

const Envoy::Http::TestRequestHeaderMapImpl request_headers_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about default_request_headers_?

* be used to inspect the response.
*/
Envoy::IntegrationStreamDecoderPtr getResponse(absl::string_view request_level_config,
bool setup_for_upstream_request);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reduce the number of public methods by half and eliminate the need to figure out the meaning of true or false in the implementation by creating a mode enum with values kUpstream and kExtension? Or if it's more convenient for the test authors to have the separate public methods, a private enum could still make the impl easier to read.

Envoy::IntegrationStreamDecoderPtr
HttpFilterIntegrationTestBase::getResponse(absl::string_view request_level_config,
bool setup_for_upstream_request) {
const Envoy::Http::LowerCaseString key("x-nighthawk-test-server-config");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to mention in all doc comments that request_level_config is sent as the x-nighthawk-test-server-config header -- I didn't realize how it worked.

* @return Envoy::IntegrationStreamDecoderPtr Pointer to the integration stream decoder, which can
* be used to inspect the response.
*/
Envoy::IntegrationStreamDecoderPtr getResponseFromUpstream();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we missing getResponseFromExtension() with no args, or does that one not make sense?

@eric846 eric846 added waiting-for-changes A PR waiting for comments to be resolved and changes to be applied. and removed waiting-for-review A PR waiting for a review. labels Sep 11, 2020
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
…r-prelude

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Copy Markdown
Member Author

oschaaf commented Sep 12, 2020

Thanks for the review @eric846. Following some of your suggestions, I refactored this some more to further simplify in
8d30820. I think it captures most of your comments modulo the one about default_request_headers_, which no longer makes sense as it is now private and used in a stateful manner. If you want, it's possible to take a peek at an example that consumes the state as it is now proposed here over here.

@oschaaf oschaaf added waiting-for-review A PR waiting for a review. and removed waiting-for-changes A PR waiting for comments to be resolved and changes to be applied. labels Sep 12, 2020
@eric846
Copy link
Copy Markdown
Contributor

eric846 commented Sep 12, 2020

LGTM, thanks!

…r-prelude

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Copy Markdown
Member Author

oschaaf commented Sep 14, 2020

@eric846 Sorry -- added a small new helper here in 7eeb7e9 which I turned out to need, PTAL.

oschaaf added a commit to oschaaf/nighthawk that referenced this pull request Sep 14, 2020
Split out from envoyproxy#512

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>

------

Prerequisite:

- [ ] Land envoyproxy#517
*
* @param config configuration to pass to Envoy::HttpIntegrationTest::config_helper_.addFilter.
*/
void setup(const std::string& config);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having a setup and a SetUp is confusing, especially since it's not really consistent with our capitalization scheme. Can we rename this to something like initializeConfig?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

: HttpIntegrationTest(Envoy::Http::CodecClient::Type::HTTP1, ip_version),
request_headers_({{":method", "GET"}, {":path", "/"}, {":authority", "host"}}) {}

void HttpFilterIntegrationTestBase::setup(const std::string& config) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check - this can't be an absl::string_view?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done -- this was slightly more convenient in the implementation as we need to copy, but I agree consistency outweighs that. Thanks for catching

const bool is_post = request_headers_.Method()->value().getStringView() ==
Envoy::Http::Headers::get().MethodValues.Post;
const uint64_t request_body_size = is_post ? 1024 : 0;
if (expected_origin == ResponseOrigin::UPSTREAM) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having trouble understanding this function's full intent. There are a lot of conditionals that seem to make the behavior very different. Is this actually common behavior across filters?

Maybe some comments in this section about why things are happening would be helpful.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments in ca17f41. Does that provide the type of context you'd consider helpful when reading this code?
Basically, this support call is an extract of the what we need to cover the stream codec code for all our extensions. We'll later see that this allows us deprecate much more complex code (which was the way to do it a while back), as wel as deduplicate some similar code.

Copy link
Copy Markdown
Member Author

@oschaaf oschaaf Sep 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, for some more concrete context, #533 is the direct follow up to this (currently in draft, as it depends on this PR, and will lean up once we merge this one and merge the result into that). It brings in a new test file which is using this extensively.
That test file adds tests for ensuring basic behavioural guarantees across all of our extensions.

The behaviours we are looking for involve certain configuration (error) handling, as well as handling default/empty configuration, both static from disk as well as dynamically delivered via response headers. These tests are parameterised over GET and POST requests.

Once we land that, we can in turn start purging / enhancing the other test files, and make them more about just testing the functionality that the extension brings in. Where applicable, it will also use the functionality we add in this PR, and we can purge their own variant(s).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(NOTE: the changes from the last paragraph above, are already staged over at #512)

@dubious90 dubious90 added waiting-for-changes A PR waiting for comments to be resolved and changes to be applied. and removed waiting-for-review A PR waiting for a review. labels Sep 14, 2020
…r-prelude

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Copy Markdown
Member Author

oschaaf commented Sep 14, 2020

@dubious90 feedback address in ca17f41, thanks for the review!

@oschaaf oschaaf added waiting-for-review A PR waiting for a review. and removed waiting-for-changes A PR waiting for comments to be resolved and changes to be applied. labels Sep 14, 2020
@dubious90 dubious90 self-assigned this Sep 15, 2020
Copy link
Copy Markdown
Contributor

@dubious90 dubious90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding all of the comments. I understand what is happening here a lot more now. Have some suggestions on naming and documentation, but as always, open to pushback.

namespace Nighthawk {

/**
* Base class with shared functionality for testing Nighthawk test server http filter extensions.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add two notes/clarifications here: 1. This class is stateful. 2. Unless I'm mistaken I don't think this class is threadsafe

HttpFilterIntegrationTestBase(Envoy::Network::Address::IpVersion ip_version);

/**
* We don't override SetUp(): tests using this fixture must call setup() instead.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: reference to setup() here is no longer accurate

// This is useful, because emitting an entity body will hit distinct code in extensions. Hence we
// facilitate that.
const uint64_t request_body_size = is_post ? 1024 : 0;
// An extension can either act as an origin and synthesize a reply, or delegate that
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through again, these comments are very helpful. Thank you for adding them

absl::string_view header_value);

/**
* Fetch a response. The request headers default to a minimal GET, but this may be changed
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rephrase this comment a little (correct anything here that is inaccurate):
Fetch a response, according to the options specified by the class methods. By default, simulates a GET request with minimal headers.

* We don't override SetUp(): tests using this fixture must call setup() instead.
* This is to avoid imposing the need to create a fixture per filter configuration.
*
* @param config configuration to pass to Envoy::HttpIntegrationTest::config_helper_.addFilter.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this configuration for the filter being tested? If so, can we clarify "filter configuration"

/**
* Indicate the expected response origin.
*/
enum class ResponseOrigin { UPSTREAM, EXTENSION };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a one line summary here for each type of ResponseOrigin? This may also allow you to remove a comment elsewhere, if it's duplication at this point.

* @param request_level_config Configuration to be delivered by request-header in future calls to
* getResponse(). For example: "{response_body_size:1024}".
*/
void updateRequestLevelConfiguration(absl::string_view request_level_config);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit/optional: How do you feel about changing this to setRequestLevelConfiguration? I think it'd then be more consistent with setRequestHeader

* Switch getResponse() to use the POST request method with an entity body.
* Doing so will make tests hit a different code paths in extensions.
*/
void switchToPostWithEntityBody();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought, curious what you think - it might be more self-documenting here, if we take in a parameter for the post body size. Or even then, rename this to setPostBodySize, with an indication that unless this is called, the requests into the filter will be GET requests.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be more up for doing this, but rather do it once a functional need for it arises (which is very possible). Right now, in the context of this PR and the changes coming after this, I'm on a mission to minimize and contain complexity that has organically grown while we added the test server and the two new extensions. Part of effort is making everything go through this hopefully well defined but minimal functionality. It should be trivial to refactor this later on according to what you suggest, once the need arises in new tests?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I'm happy to push it through as is, and if it comes up, do the refactor as you suggest.

…r-prelude

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Copy Markdown
Member Author

oschaaf commented Sep 16, 2020

@dubious90 feedback addressed in dubious90 modulo one thing, I will reply to that specific comment to explain.

Copy link
Copy Markdown
Contributor

@dubious90 dubious90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you for addressing my concerns.

@dubious90 dubious90 merged commit 5876039 into envoyproxy:master Sep 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-for-review A PR waiting for a review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants